3D オブジェクトのマテリアル ( 表面材質 ) のお世話 後編 ( Android OpenGL フレームワーク “Rajawali” と戯れる #04 )
Android OpenGL フレームワーク "Rajawali" と戯れるシリーズ
第 04 回目は、引き続き Rajawali が提供しているマテリアル ( 表面材質 ) のクラスについて説明します。
環境マッピング
環境マッピング (environment mapping) とは、3DCG におけるテクスチャマッピングの手法の 1 つで、3D 形状の表面に擬似的な周囲環境の映り込みを再現する手法です。詳しい説明はこちら (Wikipedia) を参考にしてください。
Rajawali では、下記 2 種類の環境マッピング用マテリアルクラスが用意されています。
CubeMapMaterial
キューブマップのマテリアルです。
対象のオブジェクトの 6 方向 ( +x, -x, +y, -y, +z, -z ) のイメージやライティング効果を表現したテクスチャとして 3D オブジェクトに適用します。
SphereMapMaterial
球状マップのマテリアルです。
対象のオブジェクトの周囲の空間やライティング効果を表現した特殊テクスチャとして 3D オブジェクトに適用します。
環境マッピング用テクスチャの作り方
池田先生のブログで紹介されていた Pano2VR のトライアル版をお試しで使ってみたのですがイイ感じです。
作業の流れについては下記記事を参考にしてください。
CubicVRをPapervision3D 2.0で作るPart.02(Cube編)
CubicVRをPapervision3D 2.0で作るPart.03(Sphere編)
バンプマッピング
バンプマッピング (bump mapping) とは、レンダリングする 3D オブジェクトの面の法線に対する揺らぎをハイトマップ ( 高低マップ ) で調べて、光源計算の完了前に各ピクセルに対して適用する技術です。詳しい説明はこちら (Wikipedia) を参考にしてください。
Rajawali では、BumpmapMaterial という マテリアルクラスが用意されています。
実装例
CubeMapMaterial
Lesson03_01Renderer.java
package jp.classmethod.sample.renderer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import jp.classmethod.sample.R; import rajawali.lights.DirectionalLight; import rajawali.materials.CubeMapMaterial; import rajawali.materials.SimpleMaterial; import rajawali.materials.TextureInfo; import rajawali.materials.TextureManager.TextureType; import rajawali.materials.TextureManager.WrapType; import rajawali.primitives.Plane; import rajawali.primitives.Sphere; import rajawali.renderer.RajawaliRenderer; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; /** * RajawaliRenderer のサブクラス */ public class Lesson03_01Renderer extends RajawaliRenderer { /** * コンストラクタ */ public Lesson03_01Renderer(Context context) { super(context); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { super.onSurfaceCreated(gl, config); Resources r = mContext.getResources(); // Background Bitmap bg = BitmapFactory.decodeResource(r, R.drawable.background); TextureInfo backgroundTextureInfo = mTextureManager.addTexture(bg); backgroundTextureInfo.setWrapType(WrapType.CLAMP); SimpleMaterial simpleMaterial = new SimpleMaterial(); simpleMaterial.addTexture(backgroundTextureInfo); Plane plane = new Plane(5, 10, 1, 1); plane.setRotZ(-90); plane.setPosition(0.0f, 0.0f, 1.0f); plane.setMaterial(simpleMaterial, true); // CubeMapMaterial Bitmap[] textures = new Bitmap[6]; int i = 0; textures[i++] = BitmapFactory.decodeResource(r, R.drawable.background_cuberight); // +X textures[i++] = BitmapFactory.decodeResource(r, R.drawable.background_cubeleft); // -X textures[i++] = BitmapFactory.decodeResource(r, R.drawable.background_cubetop); // +Y textures[i++] = BitmapFactory.decodeResource(r, R.drawable.background_cubebottom);// -Y textures[i++] = BitmapFactory.decodeResource(r, R.drawable.background_cubefront); // +Z textures[i++] = BitmapFactory.decodeResource(r, R.drawable.background_cubeback); // -Z TextureInfo cubeTextureInfo = mTextureManager.addCubemapTextures(textures); cubeTextureInfo.setTextureType(TextureType.CUBE_MAP); cubeTextureInfo.setWidth(300); cubeTextureInfo.setHeight(300); CubeMapMaterial cubeMapMaterial; cubeMapMaterial = new CubeMapMaterial(); cubeMapMaterial.addTexture(cubeTextureInfo); DirectionalLight light = new DirectionalLight(); light.setPower(2f); light.setPosition(-0.5f, 0.3f, -5.0f); Sphere sphere = new Sphere(1.0f, 36, 36); sphere.setMaterial(cubeMapMaterial); sphere.addLight(light); addChild(plane); addChild(sphere); startRendering(); } }
RajawaliRenderer が保持している mTextureManager というテクスチャ制御マネージャのインスタンスを使用して、テクスチャ情報 (TextureInfo) を作成します。用意したテクスチャ情報をマテリアルにセットしたあと setMaterial() すれば完成…というシンプルな実装方法です。ちなみにキューブマップでは、テクスチャ画像を 6 つ用意する必要があります。
SphereMapMaterial
Lesson03_02Renderer.java
package jp.classmethod.sample.renderer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import jp.classmethod.sample.R; import rajawali.lights.DirectionalLight; import rajawali.materials.SimpleMaterial; import rajawali.materials.SphereMapMaterial; import rajawali.materials.TextureInfo; import rajawali.materials.TextureManager.TextureType; import rajawali.materials.TextureManager.WrapType; import rajawali.primitives.Plane; import rajawali.primitives.Sphere; import rajawali.renderer.RajawaliRenderer; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; /** * RajawaliRenderer のサブクラス */ public class Lesson03_02Renderer extends RajawaliRenderer { /** * コンストラクタ */ public Lesson03_02Renderer(Context context) { super(context); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { super.onSurfaceCreated(gl, config); Resources r = mContext.getResources(); // Background Bitmap bg = BitmapFactory.decodeResource(r, R.drawable.background); TextureInfo backgroundTextureInfo = mTextureManager.addTexture(bg); backgroundTextureInfo.setWrapType(WrapType.CLAMP); SimpleMaterial simpleMaterial = new SimpleMaterial(); simpleMaterial.addTexture(backgroundTextureInfo); Plane plane = new Plane(5, 10, 1, 1); plane.setRotZ(-90); plane.setPosition(0.0f, 0.0f, 1.0f); plane.setMaterial(simpleMaterial, true); // SphereMapMaterial SphereMapMaterial material = new SphereMapMaterial(); material.setSphereMapStrength(.4f); Bitmap sphereMap = BitmapFactory.decodeResource(r, R.drawable.background); // Bitmap texture = BitmapFactory.decodeResource(r, R.drawable.hirai); DirectionalLight light = new DirectionalLight(); light.setPower(2f); light.setPosition(-0.5f, 0.3f, -5.0f); Sphere sphere = new Sphere(1.0f, 36, 36); sphere.setMaterial(material); sphere.setColor(0xFF999999); sphere.addLight(light); sphere.addTexture(mTextureManager.addTexture(sphereMap, TextureType.SPHERE_MAP)); // sphere.addTexture(mTextureManager.addTexture(texture, TextureType.DIFFUSE)); sphere.setRotY(45); addChild(plane); addChild(sphere); startRendering(); } }
BumpmapMaterial
Lesson03_03Renderer.java
package jp.classmethod.sample.renderer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import jp.classmethod.sample.R; import rajawali.BaseObject3D; import rajawali.animation.Animation3D; import rajawali.animation.RotateAroundAnimation3D; import rajawali.lights.PointLight; import rajawali.materials.BumpmapMaterial; import rajawali.materials.TextureManager.TextureType; import rajawali.math.Number3D; import rajawali.math.Number3D.Axis; import rajawali.primitives.Plane; import rajawali.renderer.RajawaliRenderer; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; /** * RajawaliRenderer のサブクラス */ public class Lesson03_03Renderer extends RajawaliRenderer { /** * @private */ private Plane mPlane; /** * コンストラクタ */ public Lesson03_03Renderer(Context context) { super(context); setFrameRate(60); setBackgroundColor(0xffffff); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { super.onSurfaceCreated(gl, config); Resources r = mContext.getResources(); mCamera.setPosition(0, 0, -6); PointLight light = new PointLight(); light.setPosition(-2, -2, -8); light.setPower(4f); mPlane = new Plane(8, 8, 1, 1);//objParser.getParsedObject(); mPlane.addLight(light); mPlane.setRotX(-45); mPlane.setRotZ(-90); addChild(mPlane); Bitmap diffuseTexture = BitmapFactory.decodeResource(r, R.drawable.hirai_bg); Bitmap bumpTexture = BitmapFactory.decodeResource(r, R.drawable.hirai_bump); mPlane.setMaterial(new BumpmapMaterial()); mPlane.addTexture(mTextureManager.addTexture(diffuseTexture, TextureType.DIFFUSE)); mPlane.addTexture(mTextureManager.addTexture(bumpTexture, TextureType.BUMP)); Animation3D lightAnimation = new RotateAroundAnimation3D(new Number3D(0, 0, -4), Axis.Z, 4); lightAnimation.setDuration(3000); lightAnimation.setRepeatCount(Animation3D.INFINITE); lightAnimation.setTransformable3D(light); lightAnimation.start(); } @Override public void onDrawFrame(GL10 glUnused) { super.onDrawFrame(glUnused); rotateObject(mPlane); } /** * 3D オブジェクトの回転 * @param obj 任意の BaseObject3D オブジェクト */ protected void rotateObject(BaseObject3D obj) { obj.setRotation(obj.getRotX(),obj.getRotY(), obj.getRotZ() + 0.1f); } }
出力結果
CubeMapMaterial
SphereMapMaterial
TextureType.SPHERE_MAP + TextureType.DIFFUSE
BumpmapMaterial
TextureType.BUMP + TextureType.DIFFUSE
若干「コレジャナイ感」を感じますが…
次回
パーティクルについて解説します。